/*!
 * Node-style module v1.0.0 of jRaiser javascript library
 * http://code.google.com/p/jraiser/
 *
 * Author: Heero.Luo
 * Licensed under MIT license
 *
 * Date: Tue Jun 05 2012 10:38:38 GMT+0800
 */
 define(function(require, exports, module) { 'use strict';

var core = require('core/1.0.x/core'), $base = require('node-base/1.0.x/node-base');


// 检查节点是否支持样式操作
function isSupportStyle(node) {
	return !($base.isWindow(node) || $base.isXMLNode(node) || node.nodeType !== 1);
}


/**
 * 样式访问（element.style）
 */
 
// 特性测试元素
var testElt = document.documentElement;

// 特殊属性名
var cssProps = {
	'float' : 'cssFloat' in testElt.style ? 'cssFloat' : 'styleFloat'
};
// 不以px为单位的数值样式
var cssNumber = {
	fillOpacity : 1,
	fontWeight : 1,
	lineHeight : 1,
	opacity : 1,
	orphans : 1,
	widows : 1,
	zIndex : 1,
	zoom : 1
};

// 特殊样式访问
var cssHooks = { };
if ( !('opacity' in testElt.style) ) {
	var OPACITY_VALUE = /opacity=([^)]*)/, ALPHA_FILTER = /alpha\([^)]*\)/i;
	
	// 获取/设置透明度，Only For IE<9
	cssHooks.opacity = {
		get : function(node) {
			return OPACITY_VALUE.test(node.currentStyle.filter || '') ?
				parseFloat(RegExp.$1) / 100 + '' : '';
		},
		set : function(node, val) {
			var filter = node.currentStyle.filter || '', style = node.style;

			val = parseFloat(val);
			
			style.zoom = 1;

			if ( (isNaN(val) || val >= 1) && filter.replace(ALPHA_FILTER, '').trim() === '' ) {
				style.removeAttribute('filter');
			} else {
				var opacity = 'alpha(opacity=' + val * 100 + ')';
				style.filter = ALPHA_FILTER.test(filter) ?
					filter.replace(filter, opacity) : filter + ' ' + opacity;
			}
		}
	};
}

testElt = null;

// 获取当前样式
var getCurrentStyle = 'getComputedStyle' in window ? function(node, name) {
	return node.ownerDocument.defaultView.getComputedStyle(node, null)[name] || '';
} : function(node, name) {
	return (node.currentStyle[name] || '').toLowerCase();
};

var DASH = /-([a-z])/g;
// 修复样式名 font-weight -> fontWeight
function fixStyleName(name) {
	return cssProps[name] || name.replace(DASH, function($0, $1) {
		return $1.toUpperCase();
	});
}
// 修复样式值
function fixStyleValue(name, val) {
	// 数字默认加上px单位（如果该样式能以px为单位）
	return cssNumber[name] || '' === val || isNaN(val) ? val : val + 'px';
}

/**
 * 获取节点当前样式
 * @param {DOMElement} 节点
 * @param {String} 样式名
 * @return {String} 样式值
 */
function getStyle(node, name) {
	if (!isSupportStyle(node)) { return null; }
	
	name = fixStyleName(name);
		
	var hook = (cssHooks[name] || 1).get;
	if (hook) {
		return hook(node);
	} else {
		var curVal = node.style[name];
		if ( !core.isUndefined(curVal) ) {
			return curVal !== '' ? curVal : getCurrentStyle(node, name);
		}
	}
}
/**
 * 设置节点样式
 * @param {DOMElement} 节点
 * @param {String} 样式名
 * @param {String} 样式值
 */
function setStyle(node, name, val) {
	if ( !isSupportStyle(node) ) { return; }
	
	name = fixStyleName(name);
	val = fixStyleValue(name, val);
	
	var hook = (cssHooks[name] || 1).set;
	if (hook) {
		hook(node, val);
	} else {
		var curVal = node.style[name];
		if ( !core.isUndefined(curVal) ) {
			node.style[name] = val;
		}
	}
}


/**
 * 样式类访问（element.className）
 */
 
// 快速检查是否包含以空格隔开的子字符串
function hasString(value, input) {
	var i = value.indexOf(input);
	return i != -1 &&
		(value.charCodeAt(i - 1) || 32) === 32 &&
		(value.charCodeAt(i + input.length) || 32) === 32;
}

var explainClassName = $base.splitBySpace;

/**
 * 检查节点是否包含指定样式类
 * @param {DOMElement} 节点
 * @param {String} 样式类
 * @return {Boolean} 节点是否包含指定样式类
 */
function hasClass(node, input) {
	if (!input) { throw new Error('classname is not specified'); }
	return isSupportStyle(node) ? hasString(node.className, input) : false;
}
/**
 * 为节点添加样式类
 * @param {DOMElement} 节点
 * @param {String|Array<String>} 样式类。多个样式类用空格隔开，或者以数组传入
 * @param {Boolean} 此参数主要用于循环调用时，忽略对样式类名的重复解析
 */
function addClass(node, classes, isPass) {
	if ( !isSupportStyle(node) ) { return; }
	
	if (!isPass) { classes = explainClassName(classes); }
	if (!classes) { return; }
	
	if ('' === node.className) {
		node.className = classes.join(' ');
	} else {
		var className = node.className, i = -1, len = classes.length;
		while (++i < len) {
			if ( !hasString(className, classes[i]) ) {
				className += (' ' + classes[i]);
			}
		}
		node.className = className.trim();
	}
}
/**
 * 为节点移除样式类
 * @param {DOMElement} 节点
 * @param {String,Array<String>} 样式类。多个样式类用空格隔开，或者以数组传入
 * @param {Boolean} 此参数主要用于循环调用时，忽略对样式类名的重复解析
 */
function removeClass(node, classes, isPass) {
	if (!isSupportStyle(node)) { return; }
	
	if (!isPass) { classes = explainClassName(classes); }
	
	if (node.className) {
		if (classes) {
			var className = ' ' + node.className + ' ', i = -1, len = classes.length;
			while (++i < len) {
				className = className.replace(' ' + classes[i] + ' ', ' ');
			}
			node.className = className.trim();
		} else {
			node.className = '';
		}
	}
}
/**
 * 如果节点包含样式类，则移除；如果节点不包含样式类，则添加
 * @param {DOMElement} 节点
 * @param {String|Array<String>} 样式类。多个样式类用空格隔开，或者以数组传入
 * @param {Boolean} 此参数主要用于循环调用时，忽略对样式类名的重复解析
 */
function toggleClass(node, classes, isPass) {
	if ( !isSupportStyle(node) ) { return; }
	
	if (!isPass) { classes = explainClassName(classes); }
	
	if (classes) {
		var className = ' ' + node.className + ' ', i = -1, len = classes.length, temp;
		while (++i < len) {
			temp = ' ' + classes[i] + ' ';
			if ( -1 === className.indexOf(temp) ) {
				className += (classes[i] + ' ');
			} else {
				className = className.replace(temp, ' ');
			}
		}
		node.className = className.trim();
	}
}


/**
 * 获取节点尺寸
 * @param {DOMElement} 节点
 * @param {String} 额外部分：填充（padding），边距（margin），边框（border），多个参数用空格隔开
 * @return {Object} 节点尺寸
 *   width {Number} 宽度
 *   height {Number} 高度
 */
function getSize(node, extra) {
	if ( $base.isWindow(node) ) {		// window对象，直接取浏览器可用范围 
		node = node.ownerDocument.documentElement;
		return {
			width : node.clientWidth,
			height : node.clientHeight
		};
	} else if (node.nodeType === 9) {	// 根节点
		node = node.documentElement;
		return {
			width : node.scrollWidth,
			height : node.scrollHeight
		};
	} else if ( !node.ownerDocument || node.nodeType !== 1 || $base.isXMLNode(node) ) {
		return null;
	}

	// 获取节点尺寸（包含padding、border）
	// IE下，如果未设置宽高，clientWidth/Height的值为0，所以要用offsetWidth/Height
	var size = {
		width : node.offsetWidth,
		height : node.offsetHeight
	};

	// 计算额外部分
	extra = extra || '';
	var borderStyle = getStyle(node, 'borderStyle');
	['Left', 'Right', 'Top', 'Bottom'].forEach(function(direction, i) {
		var which = i < 2 ? 'width' : 'height';
		if (extra.indexOf('padding') === -1) {
			size[which] -= parseFloat( getStyle(node, 'padding' + direction) ) || 0;
		}
		if (extra.indexOf('border') === -1) {
			if (borderStyle && borderStyle !== 'none') {
				size[which] -= parseFloat( getStyle(node, 'border' + direction + 'Width') ) || 0;
			}
		}
		if (extra.indexOf('margin') !== -1) {
			size[which] += parseFloat( getStyle(node, 'margin' + direction) ) || 0;
		}
	});
	
	return size;
}


core.extend(exports, {
	/**
	 * See line 95
	 */
	getStyle : getStyle,
	
	/**
	 * See line 116
	 */
	setStyle : setStyle,
	
	/**
	 * See line 154
	 */
	hasClass : hasClass,
	
	/**
	 * See line 164
	 */
	addClass : addClass,
	
	/**
	 * See line 188
	 */
	removeClass : removeClass,
	
	/**
	 * See line 211
	 */
	toggleClass : toggleClass,
	
	/**
	 * See line 237
	 */
	getSize : getSize,
	
	/**
	 * node模块的扩展接口
	 */
	nodeApis : {
		/**
		 * 获取当前节点集合中第一个节点的样式值
		 * @param {String} 样式名
		 * @return {String} 样式值
		 */
		/**
		 * 设置当前节点集合中所有节点的样式值
		 * @param {String|Object} 样式名或样式字典
		 * @param {Mixed} 样式值
		 * @return {NodeList} 当前节点集合
		 */
		css : function(name, val) {
			return core.access(this, name, val, true, {
				get : getStyle,
				set : setStyle
			});
		},
	
		/**
		 * 检查当前节点集合中是否至少有一个节点包含指定CSS样式类
		 * @param {String} 样式类
		 * @return {Boolean} 是否有节点包含指定CSS样式类
		 */
		hasClass : function(className) {
			var result = false;
			
			this.each(function(node) {
				result = result || hasClass(node, className);
				return !result;
			});
			
			return result;
		},

		/**
		 * 为当前所有节点添加CSS样式类
		 * @param {String,Array<String>} 样式类。多个样式类用空格隔开，或者以数组传入
		 * @return {NodeList} 当前节点集合
		 */
		addClass : function(className) {
			className = explainClassName(className);
			this.forEach(function(node) {
				addClass(node, className, true);
			});

			return this;
		},

		/**
		 * 为当前所有节点移除CSS样式类
		 * @param {String,Array<String>} 样式类名，如果为空，则移除所有样式。
		 *                               多个样式类用空格隔开，或者以数组传入
		 * @return {NodeList} 当前节点集合
		 */
		removeClass : function(className) {
			className = explainClassName(className);
			this.forEach(function(node) {
				removeClass(node, className, true);
			});
			
			return this;
		},
	
		/**
		 * 如果节点包含样式类，则移除；如果节点不包含样式类，则添加
		 * @param {String,Array<String>} 样式类。多个样式类用空格隔开，或者以数组传入
		 * @return {NodeList} 当前节点集合
		 */
		toggleClass : function(className) {
			className = explainClassName(className);
			this.forEach(function(node) {
				toggleClass(node, className, true);
			});
			
			return this;
		},
		
		/**
		 * 获取节点集合中第一个节点的尺寸
		 * @param {String} 额外部分：填充（padding），边距（margin），边框（border），多个参数用空格隔开
		 * @return {Object} 节点尺寸
		 *   width {Number} 宽度
		 *   height {Number} 高度
		 */
		size : function(extra) { return this[0] ? getSize(this[0], extra) : null; }
	}
});

});